/*
 * This file is part of Grocy Android.
 *
 * Grocy Android is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Grocy Android is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Grocy Android. If not, see http://www.gnu.org/licenses/.
 *
 * Copyright (c) 2020-2023 by Patrick Zedler and Dominic Zedler
 */
package com.gitee.wsl.common.zxing

import android.graphics.Color
import android.os.Handler
import android.view.Gravity
import android.view.ViewGroup
import android.view.ViewStub
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.gitee.wsl.android.ext.math.dpToPx
import com.gitee.wsl.common.more.R
import com.google.zxing.integration.android.IntentIntegrator
import com.journeyapps.barcodescanner.BarcodeResult
import com.journeyapps.barcodescanner.DecoratedBarcodeView
import com.journeyapps.barcodescanner.DecoratedBarcodeView.TorchListener
import com.journeyapps.barcodescanner.camera.CameraSettings
import com.journeyapps.barcodescanner.camera.CameraSettings.FocusMode

class EmbeddedFragmentScannerZXing(
    private val fragment: Fragment,
    containerScanner: CoordinatorLayout,
    private val barcodeListener: BarcodeListener,
    private val config: ZXingScanConfig
) : EmbeddedFragmentScanner(fragment.requireActivity()), ZXingScanCaptureManager.BarcodeListener {
    private var isScannerVisible = false
    private var isTorchOn = false
    private val barcodeView: DecoratedBarcodeView
    private val capture: ZXingScanCaptureManager
    private var suppressNextScanStart = false

    init {
        // set container size
        val width: Int
        val height: Int
        if (config.qrCodeFormat && !config.takeSmallQrCodeFormat) {
            width = 200.dpToPx()
            height = 200.dpToPx()
        } else if (config.qrCodeFormat) {
            width = 180.dpToPx()
            height = 180.dpToPx()
        } else {
            width = ViewGroup.LayoutParams.MATCH_PARENT
            height = 140.dpToPx()
        }
        if (containerScanner.parent is LinearLayout) {
            val layoutParamsContainer = LinearLayout.LayoutParams(
                width, height
            )
            layoutParamsContainer.gravity = Gravity.CENTER
            containerScanner.layoutParams = layoutParamsContainer
        } else if (containerScanner.parent is RelativeLayout) {
            val layoutParamsContainer = RelativeLayout.LayoutParams(
                width, height
            )
            layoutParamsContainer.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE)
            containerScanner.layoutParams = layoutParamsContainer
            (containerScanner.parent as RelativeLayout).gravity = Gravity.CENTER_HORIZONTAL
        }

        // fill container with necessary views
        val viewStub = ViewStub(fragment.requireContext())
        val matchParent = ViewGroup.LayoutParams.MATCH_PARENT
        val layoutParamsScanner = CoordinatorLayout.LayoutParams(matchParent, matchParent)
        viewStub.layoutParams = layoutParamsScanner
        viewStub.inflatedId = R.id.decorated_barcode_view
        if (config.qrCodeFormat && !config.takeSmallQrCodeFormat) {
            viewStub.layoutResource = R.layout.partial_scanner_zxing_2d_decorated
        } else if (config.qrCodeFormat) {
            viewStub.layoutResource = R.layout.partial_scanner_zxing_2d_small_decorated
        } else {
            viewStub.layoutResource = R.layout.partial_scanner_zxing_1d_decorated
        }
        containerScanner.addView(viewStub)
        barcodeView = viewStub.inflate() as DecoratedBarcodeView
        barcodeView.viewFinder.setMaskColor(Color.TRANSPARENT)
        barcodeView.setTorchListener(object : TorchListener {
            override fun onTorchOn() {
                isTorchOn = true
            }

            override fun onTorchOff() {
                isTorchOn = false
            }
        })
        barcodeView.setTorchOff()
        /*val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(fragment.requireContext())
        val useFrontCam = sharedPreferences.getBoolean(SCANNER.FRONT_CAM, SETTINGS_DEFAULT.SCANNER.FRONT_CAM)*/
        val cameraSettings = CameraSettings()
        cameraSettings.requestedCameraId = if (config.useFrontCam) 1 else 0
        cameraSettings.focusMode = FocusMode.CONTINUOUS
        val integrator = IntentIntegrator(fragment.requireActivity())
        integrator.setDesiredBarcodeFormats(*enabledBarcodeFormats)
        barcodeView.initializeFromIntent(integrator.createScanIntent())
        barcodeView.barcodeView.cameraSettings = cameraSettings
        capture = ZXingScanCaptureManager(
            fragment.requireActivity(),
            barcodeView,
            this
        )
    }

    override fun setScannerVisibilityLive(scannerVisibilityLive: LiveData<Boolean>) {
        setScannerVisibilityLive(scannerVisibilityLive, false)
    }

    override fun setScannerVisibilityLive(
        scannerVisibilityLive: LiveData<Boolean>,
        suppressNextScanStart: Boolean
    ) {
        this.suppressNextScanStart = suppressNextScanStart
        if (scannerVisibilityLive.hasObservers()) {
            scannerVisibilityLive.removeObservers(fragment.viewLifecycleOwner)
        }
        scannerVisibilityLive.observe(
            fragment.viewLifecycleOwner,
            Observer<Boolean> observe@{ visible: Boolean ->
                isScannerVisible = visible
                if (visible) {
                    if (this.suppressNextScanStart) {
                        this.suppressNextScanStart = false
                        return@observe
                    }
                    startScannerIfVisible()
                } else {
                    stopScanner()
                }
                lockOrUnlockRotation(visible)
            })
    }

    override fun onResume() {
        if (isScannerVisible) {
            capture.onResume()
        }
    }

    override fun onPause() {
        capture.onPause()
    }

    override fun onDestroy() {
        stopScanner()
        barcodeView.setTorchOff()
        lockOrUnlockRotation(false)
    }

    override fun stopScanner() {
        capture.onPause()
        capture.onDestroy()
    }

    override fun startScannerIfVisible() {
        if (!isScannerVisible) return
        capture.onResume()
        Handler().postDelayed({ capture.decode() }, 500)
    }

    override fun toggleTorch() {
        if (isTorchOn) {
            barcodeView.setTorchOff()
        } else {
            barcodeView.setTorchOn()
        }
    }

    override fun onBarcodeResult(result: BarcodeResult) {
        if (result.text.isEmpty()) {
            startScannerIfVisible()
            return
        }
        barcodeListener.onBarcodeRecognized(result.text)
    }

    private val enabledBarcodeFormats: Array<String>
        private get() {
            val enabledBarcodeFormats = ArrayList<String>()
            /*val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(fragment.requireContext())
            val enabledBarcodeFormatsSet = sharedPrefs.getStringSet(
                SCANNER.BARCODE_FORMATS,
                SETTINGS_DEFAULT.SCANNER.BARCODE_FORMATS
            )*/
            val enabledBarcodeFormatsSet=config.enabledBarcodeFormats

            if (enabledBarcodeFormatsSet.isNotEmpty() && !config.qrCodeFilter) {
                for (barcodeFormat in enabledBarcodeFormatsSet) {
                    when (barcodeFormat) {
                        BarcodeFormats.BARCODE_FORMAT_CODE128 -> enabledBarcodeFormats.add(
                            IntentIntegrator.CODE_128
                        )

                        BarcodeFormats.BARCODE_FORMAT_CODE39 -> enabledBarcodeFormats.add(
                            IntentIntegrator.CODE_39
                        )

                        BarcodeFormats.BARCODE_FORMAT_CODE93 -> enabledBarcodeFormats.add(
                            IntentIntegrator.CODE_93
                        )

                        BarcodeFormats.BARCODE_FORMAT_EAN13 -> enabledBarcodeFormats.add(
                            IntentIntegrator.EAN_13
                        )

                        BarcodeFormats.BARCODE_FORMAT_EAN8 -> enabledBarcodeFormats.add(
                            IntentIntegrator.EAN_8
                        )

                        BarcodeFormats.BARCODE_FORMAT_ITF -> enabledBarcodeFormats.add(
                            IntentIntegrator.ITF
                        )

                        BarcodeFormats.BARCODE_FORMAT_UPCA -> enabledBarcodeFormats.add(
                            IntentIntegrator.UPC_A
                        )

                        BarcodeFormats.BARCODE_FORMAT_UPCE -> enabledBarcodeFormats.add(
                            IntentIntegrator.UPC_E
                        )

                        BarcodeFormats.BARCODE_FORMAT_QR -> enabledBarcodeFormats.add(
                            IntentIntegrator.QR_CODE
                        )

                        BarcodeFormats.BARCODE_FORMAT_PDF417 -> enabledBarcodeFormats.add(
                            IntentIntegrator.PDF_417
                        )

                        BarcodeFormats.BARCODE_FORMAT_MATRIX -> enabledBarcodeFormats.add(
                            IntentIntegrator.DATA_MATRIX
                        )

                        BarcodeFormats.BARCODE_FORMAT_RSS14 -> enabledBarcodeFormats.add(
                            IntentIntegrator.RSS_14
                        )

                        BarcodeFormats.BARCODE_FORMAT_RSSE -> enabledBarcodeFormats.add(
                            IntentIntegrator.RSS_EXPANDED
                        )
                    }
                }
            }
            if (config.qrCodeFormat && !enabledBarcodeFormats.contains(IntentIntegrator.QR_CODE) || config.qrCodeFilter) {
                enabledBarcodeFormats.add(IntentIntegrator.QR_CODE)
            }
            val mStringArray = arrayOfNulls<String>(enabledBarcodeFormats.size)
            return enabledBarcodeFormats.toArray(mStringArray)
        }

}